package jhttpp2.controlserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.UUID;  

public class ControlHandler extends Thread {
	public static final int REQUEST_ID = 0;
	public static final int REQUEST_FRIEND_LIST = 1;
	public static final int UPDATE_INFO = 2;
	public static final int GET_MY_IP = 3;
	
	public static final int MAX_PEER_RETURN = 6;
	
	ServerSocket servSock;
    int threadNumber;
    
    static Connection conn;
	static PreparedStatement pst;
	static PreparedStatement upst;
	static PreparedStatement spst;
	
	static final String USER = "root";
	static final String PASS = "khongminh123";
	
    /** Construct a Handler. */
    ControlHandler(ServerSocket s, int i) {
        super();
        servSock = s;
        threadNumber = i;
        setName("Thread " + threadNumber + " Port: " + String.valueOf(P2pServer.ECHOPORT));
        DB_Connect();
        DB_insertPeerInfo();
        DB_updatePeerInfo();
        DB_selectPeerInfo();
    }
    
    public void finalize() {
    	DB_Close();
    }

    public void run() {
        /* Wait for a connection */
        while (true){
            try {
                System.out.println( getName(  ) + " waiting");
                Socket clientSocket;
                
                // Wait here for the next connection.
                synchronized(servSock) {
                     clientSocket = servSock.accept();
                }
                
                int command = parseCommand(clientSocket);
//                System.out.println(command);
                
                switch (command) {
                case REQUEST_ID:
                	progressRequestID(clientSocket);
                	break;
                case REQUEST_FRIEND_LIST:
                	progressRequestFriendList(clientSocket);
                	break;
                case UPDATE_INFO:
                	progressUpdateInfo(clientSocket);
                	break;
                case GET_MY_IP:
                	progressGetMyIp(clientSocket);
                	break;
                default:
                	break;
                }

                System.out.println(getName(  ) + " ENDED ");
                clientSocket.shutdownOutput();
                clientSocket.close(  );
            } catch (IOException ex) {

                System.out.println(getName(  ) + ": IO Error on socket " + ex);

                return;
            }
        }
    }
    
    private void progressGetMyIp(Socket clientSocket) {
		// TODO Auto-generated method stub			
		PrintStream os;
		try {
			os = new PrintStream(clientSocket.getOutputStream(), true);
			String line;
			if( !clientSocket.getInetAddress().getHostAddress().toString().equals("127.0.0.1") )
				line = "IP=" + clientSocket.getInetAddress().getHostAddress().toString();
			else 
				line = "IP=" + Inet4Address.getLocalHost().getHostAddress().toString();
			
			os.print(line + "\r\n");
	        os.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private void progressUpdateInfo(Socket clientSocket) {
		// TODO Auto-generated method stub    	
    	BufferedReader is;
    	try {
			is = new BufferedReader(new InputStreamReader(
					clientSocket.getInputStream()));
			PrintStream os = new PrintStream(
		            clientSocket.getOutputStream(), true);
			
			String uuid = is.readLine();
			String port = is.readLine();
			
			String ip;
			if( !clientSocket.getInetAddress().getHostAddress().toString().equals("127.0.0.1") )
				ip = clientSocket.getInetAddress().getHostAddress().toString();
			else 
				ip = Inet4Address.getLocalHost().getHostAddress().toString();
			
			upst.setString(1, ip);
			upst.setString(2, port);
			upst.setString(3, uuid);
			upst.execute();
			
			String line = "UPDATE DONE";
	        os.print(line + "\r\n");
	        os.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void progressRequestFriendList(Socket clientSocket) {
		// TODO Auto-generated method stub
		int countPeer = DB_getCountPeer();
		int maxPeerId = DB_getMaxPeerId();
		int maxPeer = 0;
		
		HashMap< Integer, PeerInfo > hm = new HashMap<Integer, PeerInfo>();
		HashMap< Integer, Integer > hmrandomed = new HashMap<Integer, Integer>();
		
		if ( countPeer < MAX_PEER_RETURN ) {
			maxPeer = countPeer;
		} else {
			maxPeer = MAX_PEER_RETURN;
		}
		
		int i = 0;
		while ( i < maxPeer ) {
			if( hmrandomed.size() >= countPeer ){
				System.out.println("don't have anypeer match condition");
				break;
			}
			
			Random r = new Random( );
			//create random number
			int rand = 1 + Math.round(r.nextInt(maxPeerId));
			
			//if random number not contain this rand
			if( !hmrandomed.containsKey(rand)){
				//put its to rand
//				System.out.println(rand);
	
				try {
					spst.setInt(1, rand);
					ResultSet rs = spst.executeQuery();
					//get peer info it from db
					if ( rs.next() ) {
						hmrandomed.put(rand, 0);
						if( !hm.containsKey(rand) ){
							PeerInfo pi = new PeerInfo();
							pi.id = rand;
							pi.uuid = rs.getString("peer_uuid");
							pi.ip = rs.getString("ip");
							pi.port = rs.getString("port");
							
//							System.out.println("Ping:" + pi.ip);
							//check peer online
							//Socket sk = new Socket(pi.ip, Integer.parseInt(pi.port));
							Socket sk = new Socket();   
							sk.connect(new InetSocketAddress(pi.ip, 10002), 1000);
							
							PrintStream os = new PrintStream(
									sk.getOutputStream(), true);
							String line = "PING";
					        os.print(line + "\r\n");
					        os.flush();
					        
					        BufferedReader is = new BufferedReader(new InputStreamReader(
					        		sk.getInputStream()));
					        
					        String pingres = is.readLine();
//					        System.out.println("READ:" + pingres);
					        if( pingres.equals("ONLINE") ) { //if ping oke\
//					        	System.out.println("Add" + pi.uuid);
					        	hm.put(rand, pi);
								i++;
					        }
					        sk.close();
						}
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
//					System.out.println("error SQLException");
					e.printStackTrace();
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
//					System.out.println("error NumberFormatException");
					e.printStackTrace();
				} catch (UnknownHostException e) {
					// TODO Auto-generated catch block
//					System.out.println("error UnknownHostException");
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
//					System.out.println("error IOException");
//					e.printStackTrace();
					continue;
				}
			}
		}
		
		
		//return friend list
		PrintStream os;
		try {
			os = new PrintStream(
			        clientSocket.getOutputStream(), true);
			String line = "FRIEND_LIST";
	        os.print(line + "\r\n");
	        os.flush();
	        
			Iterator<Integer> it = hm.keySet( ).iterator( );
		    while ( it.hasNext() ) {
		    	Integer key = (Integer) it.next();
		    	PeerInfo pi = hm.get(key);
		    	
		    	line = "UUID="+ pi.uuid;
		        os.print(line + "\r\n");
		        os.flush();
		    	line = "IP="+ pi.ip;
		        os.print(line + "\r\n");
		        os.flush();
		        line = "PORT="+ pi.port;
		        os.print(line + "\r\n");
		        os.flush();
		        
		    }
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void progressRequestID(Socket clientSocket) {
		// TODO Auto-generated method stub
		System.out.println(getName(  ) + " starting, IP=" + 
                clientSocket.getInetAddress().getHostAddress().toString());
		
		BufferedReader is;
		UUID uniqueKey;
		
		try {
			is = new BufferedReader(new InputStreamReader(
					clientSocket.getInputStream()));
			String port = is.readLine();
			
			while(true) {
				uniqueKey = UUID.randomUUID();   
				System.out.println (uniqueKey.toString());
				
				if( DB_existUUID(uniqueKey.toString()) == 0 )
					break;
			}
			
			String ip;
			if( !clientSocket.getInetAddress().getHostAddress().toString().equals("127.0.0.1") )
				ip = clientSocket.getInetAddress().getHostAddress().toString();
			else 
				ip = Inet4Address.getLocalHost().getHostAddress().toString();
			
			pst.setString(1, uniqueKey.toString());
			pst.setString(2, ip);
			pst.setString(3, port);
			pst.execute();
			
			PrintStream os;
			os = new PrintStream(
			        clientSocket.getOutputStream(), true);
			String line = "UUID:" + uniqueKey.toString();
	        os.print(line + "\r\n");
	        os.flush();
	        
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public int parseCommand(Socket clientSocket) {
    	BufferedReader is;
		try {
			is = new BufferedReader(new InputStreamReader(
					clientSocket.getInputStream()));	

	        String line = is.readLine();
	        System.out.println("READED: " + line);
    		if( line.equals("REQUEST_ID") )
            	return REQUEST_ID;
    		else if( line.equals("REQUEST_FRIEND_LIST") ) {
    			return REQUEST_FRIEND_LIST;
    		} else if( line.equals("UPDATE_INFO") ) {
    			return UPDATE_INFO;
    		} else if( line.equals("GET_MY_IP") ) {
    			return GET_MY_IP;
    		} else
    			return -1;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        return -1;
    }
	
	//================db function=======================
	public void DB_Connect(){
		try{
			try {
				Class.forName("com.mysql.jdbc.Driver").newInstance();
			} catch (InstantiationException | IllegalAccessException
					| ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String url = "jdbc:mysql://localhost/peerdata";
			
			conn = DriverManager.getConnection(url, USER, PASS);
		}catch (SQLException ex) {
			ex.printStackTrace();
		}
	}
	
	public void DB_Close(){
		try {
			if( !conn.isClosed()){
				conn.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public int DB_existUUID(String uuid){
		String query = "select peer_uuid from peertables";
		try {
			Statement st = conn.createStatement();
			ResultSet rs = st.executeQuery(query);
			
			//rs.beforeFirst();
			while( rs.next() ){
				String s = rs.getString("peer_uuid");
				if( s.contentEquals(uuid) ){
					System.out.println("\t\tExist UUID Break now!");
					return 1;
				}		
			}
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -1;
			
		}
		return 0;
	}
	
	public void DB_insertPeerInfo(){
		String sql = "INSERT INTO peertables(peer_uuid, ip, port) " + 
				"VALUES(?,?,?)";
		
		try {
			pst = conn.prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void DB_updatePeerInfo(){
		String sql = "UPDATE peertables SET ip = ?, port = ? " + 
				"WHERE peer_uuid = ?";
		
		try {
			upst = conn.prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void DB_selectPeerInfo(){
		String sql = "SELECT * FROM peertables " + 
				"WHERE id = ?";
		
		try {
			spst = conn.prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public int DB_getCountPeer() {
		Statement stmt;
		try {
			stmt = conn.createStatement();
			String sql = "select count(id) from peertables; ";
	        ResultSet rs = stmt.executeQuery(sql);
	        
	        rs.first();
	        return rs.getInt("count(id)");
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return -1;
	}
	
	public int DB_getMaxPeerId() {
		Statement stmt;
		try {
			stmt = conn.createStatement();
			String sql = "select id from peertables";
	        ResultSet rs = stmt.executeQuery(sql);
	        
	        rs.last();
	        return rs.getInt("id");
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return -1;
	}
}
